Skip to content

test(frontend): a wait begins at the thing it is evidence of - #1189

Merged
ExtraToast merged 2 commits into
mainfrom
fix/specs-budget-for-their-own-delays
Sep 6, 2026
Merged

test(frontend): a wait begins at the thing it is evidence of#1189
ExtraToast merged 2 commits into
mainfrom
fix/specs-budget-for-their-own-delays

Conversation

@ExtraToast

@ExtraToast ExtraToast commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Why

Four end-to-end specs failed on a pull request that changes only Kotlin and touches no frontend
file. A red suite on an unrelated change teaches everybody to re-run rather than read the result,
which is the habit #1095 spent nine slices removing.

closes #1173

What this achieves

Two of the four are fixed, at their real causes. The other two are split out with their evidence
rather than guessed at: #1186 and #1187.

A third spec is fixed alongside, because it turned out to be the same defect as one of them.

How

The save's wait begins at the submit it is evidence of. committee-save.spec.ts opened its
waitForRequest above the navigation, so the 5000ms was spent on a page load, an edit click and a
name fill before the submit it exists to observe was ever clicked. From the failing trace: the wait
opens at +573549, goto runs to +576643, the edit click to +578302, and the budget expires at
+578549 during the description fill. The wait now opens immediately before the submit click, so its
budget covers the click and the request that click makes.

The line's landing is measured once it has landed. esports-season-drag.motion.spec.ts polled
until the strip's scroll differed from where it opened — which answers on the first frame of a
smooth scroll — and then read the arrived node's offset from centre exactly once. CI read 44 against
a bound of 24: the line was still travelling. It now polls the offset. boards-swipe.motion.spec.ts
had the identical shape and is fixed with it.

No timeout raised, no default changed, no bare wait added, and every assertion and bound is
unchanged.

Not in scope

The two that could not be reproduced, each filed with its trace evidence, what was ruled out, and an
unproven hypothesis recorded so the next reader does not derive it again: #1186 (a wheel that
scrolls nothing on mobile-chrome) and #1187 (one frame still moving after the reduced ceiling).

Worth a reviewer's attention

The issue's own diagnosis was wrong, and so was mine. #1173 supposed the 3000ms GET /users
delay ate the budget. It does not: the read is concurrent and the save never waits on it, which is
the point of the case — the test takes 520ms end to end with the delay at 3000 and 532ms with it at
0. And boards-swipe.motion.spec.ts is the test #1148's own body wrote off as "the known starvation
flake". It was this defect.

The reproduction knob had to match the mechanism, per #1141. For the save it is
launchOptions.slowMo, which lengthens the wall-clock between the wait opening and the request
without blowing any single step; CPU hogs did reproduce it, at 19 in 20, but also blew page.goto
sixteen times, which is not the CI symptom. For the two motion specs it is renderer CPU throttling
through Emulation.setCPUThrottlingRate, since a smooth scroll advances on delivered frames.

Criterion 1 generally. A setTimeout inside a route handler appears exactly once in the whole
suite, and that is this one. The other manufactured pauses are gesture cadence rather than held
routes, and none sits inside an inherited-budget wait.

Verification

before after
committee-save at slowMo 1200 20/20 failed, with the exact CI error 0/20
committee-save at slowMo 1800 0/20
esports-season-drag at 8x renderer throttle 15/15 failed, offsets 225-308px 0/15
esports-season-drag at 12x 0/15
boards-swipe at 8x 15/15 failed 0/15

Full yarn test:e2e: exit 0, 822 passed, 0 failed, 36 skipped. yarn typecheck and yarn lint
both exit 0.


Diff breakdown added removed, scaled to the largest row.

frontend                                           +19    -14    3
  e2e tests          ███████████████░░░░░░░░░░░    +19    -14    3

──────────────────────────────────────────────────────────────────
production                                          +0     -0
tests                                              +19    -14
total (hand-written)                               +19    -14  3 files

@ExtraToast ExtraToast added the bug Something isn't working label Sep 6, 2026
`waitForRequest` was opened above the navigation, so its five second budget
covered the page load and both fills as well as the click it exists to watch.
The trace of the failing run spends it exactly there: the wait opens at
+573549ms, the navigation alone takes 3.09s and the description fill starts at
+578678ms, past the 5000ms mark, so the budget was gone before the submit was
ever clicked.

The 3000ms the spec holds `GET /users` for is not what spends it — that read is
concurrent and the save does not wait on it, which is the whole point of the
case. Opened at the submit, the budget covers the click and the request it
makes.

Refs #1173
Both specs prove the strip travels by polling until its scroll position differs
from the one it opened at, which answers on the first frame of a smooth scroll,
and then read the arrived node's offset from the middle exactly once. That
reading is taken somewhere along the travel rather than at the end of it: the
esports case failed in CI at 44px against a bound of 24.

The bound is untouched and the claim is the same. Reproduced with the renderer
throttled 8x, which is what starves the frames a smooth scroll advances on:
15 of 15 runs failed at 225-308px before, 0 of 15 after, and 0 of 15 at 12x.

Refs #1173
@ExtraToast

Copy link
Copy Markdown
Contributor Author

Rebased onto main, which now carries #1190 — the same spec, fixed differently. That change kept the wait above the navigation and raised its budget to 60s; this one moves the wait to immediately before the submit and raises nothing.

Resolved in favour of the narrower fix, because #1173's first criterion is that a spec which delays a route on purpose does not spend a default timeout on that delay, and a 60s budget still spends the wait on the page load and the two fills — it just leaves more room. The reason given for opening it early, that a save landing quickly would otherwise be missed, cannot happen: the save is caused by the click, so a wait opened immediately before that click cannot miss it.

Measured rather than argued: 20 of 20 failed at slowMo 1200 with this fix reverted, 0 of 20 with it, and 0 of 20 again at slowMo 1800.

The trace evidence for the timing is in the PR body. Happy to take the 60s back if there is a case for it I have not seen.

@ExtraToast
ExtraToast force-pushed the fix/specs-budget-for-their-own-delays branch from 355269a to 60e7592 Compare September 6, 2026 19:28
@ExtraToast
ExtraToast merged commit e67677c into main Sep 6, 2026
25 checks passed
@ExtraToast
ExtraToast deleted the fix/specs-budget-for-their-own-delays branch September 6, 2026 19:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Four specs fail on a pull request that changes no frontend code

1 participant